package com.hzp.micromall.auth.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.token.TokenEnhancer;
import org.springframework.security.oauth2.provider.token.TokenEnhancerChain;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.KeyStoreKeyFactory;

import java.security.KeyPair;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

/**
 * @Author houzipeng
 * @Date 2022/2/10 18:14
 * @Version 1.0
 * 认证服务配置
 */
@Configuration
@EnableAuthorizationServer // 开启授权服务器
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
    @Autowired
    private AuthenticationManager authenticationManager;
    @Autowired
    private UserDetailsService userDetailsService;
    @Autowired
    private JwtTokenEnhancer jwtTokenEnhancer;
    @Autowired
    private PasswordEncoder passwordEncoder;

    @Value("${jwt.token.secret}")
    private String secret;

//    @Autowired
//    private RedisConnectionFactory redisConnectionFactory;

    // TODO: 2022/2/10 需要修改 由数据库来维护客户端信息
    /**
     *
     *  我们配置了使用数据库来维护客户端信息。虽然在各种 Demo 中我们经常看到的是在内存中维护客户端信息， 通过配置直接写死在这里。
     *  但是，对于实际的应用我们一般都会用数据库来维护这个信息，甚至还会建立一套工作流来允许客户端自己申请 ClientID，实现 OAuth 客户端接入的审批。
     * @param clients
     * @throws Exception
     */
//    @Override
//    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
//        clients.jdbc(dataSource);
//    }

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory()
                .withClient("admin-app")
                .secret(passwordEncoder.encode("hzp0413"))
                .scopes("all")
                .authorizedGrantTypes("password", "refresh_token")
                .accessTokenValiditySeconds(3600*24)
                .refreshTokenValiditySeconds(3600*24*7);
    }

    /**
     * 1. 配置我们的令牌存放方式为 JWT方式，而不是内存、数据库或 Redis方式。
     *    JWT 是 Json Web Token 的缩写，也就是使用 JSON 数据格式包装的令牌，由 .号把整个 JWT分隔为头、数据体、签名三部分。
     *    JWT保存 Token 虽然易于使用但是不是那么安全，一般用于内部，且需要走 HTTPS 并配置比较短的失效时间。
     * 2. 配置 JWT Token 的非对称加密来进行签名
     * 3. 配置一个自定义的 Token 增强器，把更多信息放入 Token 中
     * 4. 配置使用 JDBC 数据库方式来保存用户的授权批准记录
     * @param endpoints
     * @throws Exception
     */
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        // 配置JWT的内容增强器
        TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
        List<TokenEnhancer> delegates = new ArrayList<>();
        delegates.add(jwtTokenEnhancer);
        delegates.add(jwtAccessTokenConverter());
        tokenEnhancerChain.setTokenEnhancers(delegates);

        endpoints
                // 指定认证服务器
                .authenticationManager(authenticationManager)
                // 用户名密码验证
                .userDetailsService(userDetailsService)
                .accessTokenConverter(jwtAccessTokenConverter())
                // 自定义生成令牌
                .tokenEnhancer(tokenEnhancerChain);
                /** refresh token有两种使用方式：重复使用(true)、非重复使用(false)，默认为true
                 *  1 重复使用：access token过期刷新时， refresh token过期时间未改变，仍以初次生成的时间为准
                 *  2 非重复使用：access token过期刷新时， refresh token过期时间延续，在refresh token有效期内刷新便永不失效达到无需再次登录的目的
                 */
//                .reAdminefreshTokens(true)
    }

    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        security.tokenKeyAccess("permitAll()")
                .checkTokenAccess("permitAll()")
                .allowFormAuthenticationForClients();
    }

    /**
     * 使用非对称加密算法对token签名
     */
    @Bean
    public JwtAccessTokenConverter jwtAccessTokenConverter() {
        JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
        converter.setKeyPair(keyPair());
        return converter;
    }

    /**
     * 密钥库中获取密钥对(公钥+私钥)
     */
    @Bean
    public KeyPair keyPair() {
        KeyStoreKeyFactory factory = new KeyStoreKeyFactory(new ClassPathResource("jwt.jks"), secret.toCharArray());
        KeyPair keyPair = factory.getKeyPair("jwt", secret.toCharArray());
        return keyPair;
    }

//    /**
//     * 配置客户端详情
//     */
//    @Override
//    public void configure(ClientDetailsServiceConfigurer clients) throws Exception
//    {
//        clients.withClientDetails(clientDetailsService());
//    }
//
//    /**
//     * 声明 ClientDetails实现
//     */
//    public RedisClientDetailsService clientDetailsService()
//    {
//        RedisClientDetailsService clientDetailsService = new RedisClientDetailsService(dataSource);
//        return clientDetailsService;
//    }

//    /**
//     * 基于 Redis 实现，令牌保存到缓存
//     */
//    @Bean
//    public TokenStore tokenStore()
//    {
//        RedisTokenStore tokenStore = new RedisTokenStore(redisConnectionFactory);
//        tokenStore.setPrefix(CacheConstants.OAUTH_ACCESS);
//        return tokenStore;
//    }
}
